#include <SPI.h>
#include <GD.h>
#include <Wire.h>

#include "pongback.h"
#include "sprite.h"
#include "mont.h"

#define CONTROL_LEFT  1
#define CONTROL_RIGHT 2    
#define CONTROL_UP    4
#define CONTROL_DOWN  8
#define RADIUS 2
#define YBASE 256
#define BG_ZERO 125
#define BG_BLACK 16
#define MAX_LIFE 5
#define BG_BALL 135
#define MBLOCK 10


//variaveis para o som
static byte amp[64];       
static byte target[64];
//----------------
static uint16_t lfsr = 1;
static uint8_t outbuf[6];		
int cnt = 0;
static float xbarra = 190;
static float xv=0.5,yv=0.5;
static float x=0, y=0;
static int score = 0;
static int lifes;
static int nblock;
static int level=1;
static int lvlup;
static int z_botao, go, limp_ball, hscore;
static int freq_lvlup[11] = {1000, 2000, 3000, 4000, 5000, 6000, 5000, 2000, 2700, 4100, 1800};
static int freq_dead[11] = {100, 200, 300, 400, 500, 600, 500, 200, 270, 410, 180};

// ----------------------------------------------------------------------
//     controller: buttons on Arduino pins 3,4,5,6
// ----------------------------------------------------------------------
byte controller_sense(void)
{
  int horizontalSensor = analogRead(0);
  int verticalSensor = analogRead(1);
  byte r = 0;
  //Serial.println(horizontalSensor, DEC);
  if(horizontalSensor > 430)
    r = CONTROL_LEFT; 
  if(horizontalSensor < 390)
    r = CONTROL_RIGHT;   
  if(verticalSensor < 360)
    r = CONTROL_UP; 
  return r;}
//-------------------------------------------------------------------------------------------------
//--BLOCOS---------------------
//-------------------------------------------------------------------------------------------------
struct block {
  int x, y;
  byte colisao, anim;
};
static struct block blocks[MBLOCK];

static void colidiu()
{
  for(int i = 0;i < nblock;i++){
    if(blocks[i].colisao && ((x == blocks[i].x + 8 + RADIUS  && y <= blocks[i].y - 3 - RADIUS && y >= blocks[i].y - 8 - RADIUS) || (x == blocks[i].x - 8 - RADIUS && y <= blocks[i].y - 3 - RADIUS && y >= blocks[i].y - 8 - RADIUS))) //lados do bloco
    {
       xv = -xv; 
       blocks[i].colisao = 0;
       blocks[i].anim = 10;
       lvlup--;
       score = score+10;    
       
       
    }
    
    if(blocks[i].colisao && ((x <= blocks[i].x + 8 + RADIUS && x >= blocks[i].x - 8 - RADIUS && y == blocks[i].y - 8 - RADIUS) ||( x <= blocks[i].x + 8 + RADIUS && x >= blocks[i].x - 8 - RADIUS && y == blocks[i].y - 3 - RADIUS)))//topo e base do bloco
    {
       yv = -yv; 
       blocks[i].colisao = 0;
       blocks[i].anim = 10;
       lvlup--;
       score = score+10;      
    }
    
    
    
  }
}

//-------------------------------------------------------------------------------------------------
//----SOUND FUNCTIONS ---------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
static void squarewave(uint16_t freq, byte amp)
{
  GD.voice(0, 0, freq,     amp,    amp);
  GD.voice(1, 0, 3 * freq, amp/3,  amp/3);
  GD.voice(2, 0, 5 * freq, amp/5,  amp/5);
  GD.voice(3, 0, 7 * freq, amp/7,  amp/7);
  GD.voice(4, 0, 9 * freq, amp/9,  amp/9);
  GD.voice(5, 0, 11 * freq, amp/11,  amp/11);
}

void setvol(byte v, byte a)
{
  GD.__wstart(VOICES + (v << 2) + 2);
  SPI.transfer(a);
  SPI.transfer(a);
  GD.__end();
}

void adsr()  
{
  byte v;
  for (v = 0; v < 64; v++){
    int d = target[v] - amp[v]; 
    if (d) {
      if (d > 0)
        amp[v] += 4;
      else
        amp[v]--;     
      setvol(v, amp[v]);
    }
  }
}



//-------------------------------------------------------------------------------------------------
static uint16_t atxy(byte x, byte y)
{
  return RAM_PIC + 64 * (y+2) + (x + 11);
}

static void draw_score(uint16_t dst, long n, long m)
{ //SCORE
  GD.wr(dst + 0, BG_ZERO + (n / 10000) % 10);   // ten-thousands
  GD.wr(dst + 1, BG_ZERO + (n / 1000) % 10);    // thousands
  GD.wr(dst + 2, BG_ZERO + (n / 100) % 10);     // hundreds
  GD.wr(dst + 3, BG_ZERO + (n / 10) % 10);      // tens
  GD.wr(dst + 4, BG_ZERO + n % 10);  // ones
  //H-SCORE
  GD.wr(dst + 11, BG_ZERO + (m / 10000) % 10);   
  GD.wr(dst + 12, BG_ZERO + (m / 1000) % 10);    
  GD.wr(dst + 13, BG_ZERO + (m / 100) % 10);    
  GD.wr(dst + 14, BG_ZERO + (m / 10) % 10);      
  GD.wr(dst + 15, BG_ZERO + m % 10);  
  
  
  for(int k = 0;k < 6; k++)
    GD.wr(dst + 5+k,BG_BLACK);
}


void setup()
{
  GD.begin();
  Serial.begin (9600);
  //controller_init();//iniciar nunchuck
  
  //sprite
  GD.copy(RAM_SPRPAL, sprite_sprpal, sizeof(sprite_sprpal));
  GD.copy(RAM_SPRIMG, sprite_sprimg, sizeof(sprite_sprimg));
  
  
  //BG
  GD.copy(RAM_CHR, pongback_chr, sizeof(pongback_chr));
  GD.copy(RAM_PAL, pongback_pal, sizeof(pongback_pal));
  GD.fill(RAM_PIC,RGB(255,255,255),4096);
  
  
  for (byte y = 0; y < 35; y++)                    
    GD.copy(atxy(0,y), pongback_pic + y * 29, 29);
    
  for(int k = 0;k < 4; k++)
    GD.wr(atxy(0,33) + k + 8 ,BG_BLACK);
 
 
 lifes = MAX_LIFE;
 level_start(level); 
 
}

static void draw()
{  
  int j = xbarra;
  
  GD.__wstartspr(0); 
  
  for(int i = 1; i<5;i++){ //barra
   draw_sprite(j, 256, i, 0);
   j+=16;
  }
  
  for (int i = 0; i < nblock; i++)
       draw_sprite(blocks[i].x, blocks[i].y, blocks[i].anim, 0); //blocos
     
   
  if(hscore < score)
    hscore = score;
    
  draw_sprite(x,y,limp_ball,0); //bola
  draw_score(atxy(0,34), score, hscore); //score  
  draw_life(atxy(0,34), lifes);  //vida
  GD.__end();}

void loop()
{
  byte control_barra = controller_sense();
  
  if(lvlup <= 0){
  level=1;
  if(level > 5)
    level = 1;
    for(int i = 0; i < 11; i++){
      squarewave(freq_lvlup[i],     255);
      delay(20);}
  level_start(level);}
  

      
   if(control_barra == CONTROL_UP){
     go = 1;}  
   if(go){
     x += xv;
     y += yv;}   
   else{
      x = xbarra + 23;
      y = 249;}
  //---------------------------------
  
  
  if(((xbarra - 1 < 96) ? 0 : 1)){       //limite minimos barra 
    if (control_barra == CONTROL_LEFT) {
          xbarra-=0.5;}}
  if(((xbarra + 48 > 313) ? 0 : 1)){      //limite maximo barra
    if (control_barra == CONTROL_RIGHT) {
          xbarra+=0.5;}}
  
    if ((x < 100) || (x > (314 - RADIUS))){ //bateu laterais
      xv = -xv;
      for(int i = 0; i < 50;i++) 
        squarewave(3000,255);}
    else
      squarewave(0, 0);  
    
    if(y < 28){                           //bateu topo BG
      yv = -yv;
      for(int i = 0; i < 50;i++)     
        squarewave(3000,255);}
    else
      squarewave(0, 0);
    
    if (y + 1 > 280){         //dead, passou linha vermelha
      lifes--;
      for(int i = 0; i < 11; i++){
          squarewave(freq_dead[i],255);
          delay(20);} 
      go = 0;    
      if(lifes == 0){
        level = 1;
        lifes = MAX_LIFE;
        score = 0;
        level_start(level);
        
        //GAME-OVER
        /*for(int i = 0; i < 3; i++)
          GD.wr(atxy(10 + i,15), i + 12); //GAM
          GD.wr(atxy(8  + 5,15), 11); //E
          GD.wr(atxy(9  + 5,15), 17); //-
          GD.wr(atxy(10 + 5,15), 9);//O
          GD.wr(atxy(11 + 5,15), 15);//V
          GD.wr(atxy(12 + 5,15), 11);//E
          GD.wr(atxy(13 + 5,15), 10); //R*/
          //sound_abertura();
          
          //limpando game-over
          /*for(int i = 10; i < 20;i++)
            GD.wr(atxy(i,15), BG_BLACK);*/
            
          delay(1000);
          
        }
    }      
    
    if ((y  == 250)  && (x > xbarra + 5) && (x < xbarra + 51)){    //tocou na barra
        yv = -yv;                         
        }
  colidiu();   
  draw();

}

void draw_life(uint16_t dsb, long life)
{
  
  int p = 0;
  
  for(int f = 0; f < MAX_LIFE - life; f++)
  {  
    GD.wr(dsb + 23 + p, BG_BLACK);
    p+=1;
   }
}
void level_start(int nivel)
{
  
  int o = 0; 
  go = 0;
  
  // limpando os blocos
  for(int i = 0; i < MBLOCK;i++){
    blocks[i].colisao = 1;
    blocks[i].anim = 10;
    blocks[i].x =  0;
    blocks[i].y = 0;}
  
  //limpando a bola
  x = 0;
  y =0;
  limp_ball = 10;
  delay(500);
  draw();
  limp_ball = 0; 
  
     
  if(nivel == 1){ //Level 1
  nblock = 2;
  blocks[0].x = 200;
  blocks[0].y = 150;
  blocks[0].anim = 7;
  
  blocks[1].x = 200;
  blocks[1].y = 75; 
  blocks[1].anim = 8;}

  lvlup = nblock;
  
  for(int l = 0; l < MAX_LIFE; l++)
  {  
   GD.wr(atxy(0,34) + 23 + o, BG_BALL);
   o+=1;
  }
}
